/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.executor.loader.javassist;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.Proxy;
import javassist.util.proxy.ProxyFactory;

import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.loader.AbstractEnhancedDeserializationProxy;
import org.apache.ibatis.executor.loader.AbstractSerialStateHolder;
import org.apache.ibatis.executor.loader.ResultLoaderMap;
import org.apache.ibatis.executor.loader.WriteReplaceInterface;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.property.PropertyCopier;
import org.apache.ibatis.reflection.property.PropertyNamer;
import org.apache.ibatis.session.Configuration;

/**
 * @author Eduardo Macarron
 */
/**
 * Javassist延迟加载代理工厂
 */
public class JavassistProxyFactory implements org.apache.ibatis.executor.loader.ProxyFactory {

	private static final Log log = LogFactory.getLog(JavassistProxyFactory.class);
	private static final String FINALIZE_METHOD = "finalize";
	private static final String WRITE_REPLACE_METHOD = "writeReplace";

	public JavassistProxyFactory() {
		try {
			// 先检查是否有javassist
			Resources.classForName("javassist.util.proxy.ProxyFactory");
		} catch (Throwable e) {
			throw new IllegalStateException(
					"Cannot enable lazy loading because Javassist is not available. Add Javassist to your classpath.",
					e);
		}
	}

	@Override
	public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration,
			ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
		return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory,
				constructorArgTypes, constructorArgs);
	}

	/**
	 * 创建反序列化代理
	 * 
	 * @param target
	 *            要代理的对象类型
	 * @param unloadedProperties
	 *            不加载的属性集
	 * @param objectFactory
	 *            对象工厂
	 * @param constructorArgTypes
	 *            构造函数的参数类型
	 * @param constructorArgs
	 *            构造函数的参数值
	 * 
	 * @return
	 */
	public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
			ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
		return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory,
				constructorArgTypes, constructorArgs);
	}

	@Override
	public void setProperties(Properties properties) {
		// Not Implemented
	}

	/**
	 * 创建代理
	 * 
	 * @param type
	 *            原生的对象类型
	 * @param callback
	 *            回调方法，就是实现了MethodInterceptor接口的子类，会有accept方法
	 * @param constructorArgTypes
	 *            构造函数的参数类型
	 * @param constructorArgs
	 *            构造函数的参数值
	 * @return
	 */
	static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes,
			List<Object> constructorArgs) {

		// 核心就是用javassist的ProxyFactory,没啥可说的，下面逻辑都是cglib的翻版
		ProxyFactory enhancer = new ProxyFactory();
		// 要生成代理对象的原生类
		enhancer.setSuperclass(type);

		try {
			// 获取目标类型的 writeReplace 方法，如果没有，异常中代理类设置enhancer.setInterfaces(new
			type.getDeclaredMethod(WRITE_REPLACE_METHOD);
			// ObjectOutputStream will call writeReplace of objects returned by
			// writeReplace
			log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
		} catch (NoSuchMethodException e) {
			enhancer.setInterfaces(new Class[] { WriteReplaceInterface.class });
		} catch (SecurityException e) {
			// nothing to do here
		}

		Object enhanced = null;
		Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
		Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
		try {
			// 创建代理对象
			enhanced = enhancer.create(typesArray, valuesArray);
		} catch (Exception e) {
			throw new ExecutorException("Error creating lazy proxy.  Cause: " + e, e);
		}
		((Proxy) enhanced).setHandler(callback);
		return enhanced;
	}

	private static class EnhancedResultObjectProxyImpl implements MethodHandler {
		/**
		 * 要代理的对象类型
		 */
		private Class<?> type;
		/**
		 * 懒加载集
		 */
		private ResultLoaderMap lazyLoader;
		/**
		 * 当启用时, 有延迟加载属性的对象在被调用时将所有属性都进行加载。否则, 该对象的每个属性将会按需加载。。
		 */
		private boolean aggressive;
		/**
		 * 懒加载触发方法集
		 */
		private Set<String> lazyLoadTriggerMethods;
		/**
		 * 对象工厂
		 */
		private ObjectFactory objectFactory;
		/**
		 * 构造函数的参数类型
		 */
		private List<Class<?>> constructorArgTypes;
		/**
		 * 构造函数的参数值
		 */
		private List<Object> constructorArgs;

		/**
		 * 构造函数
		 * 
		 * @param type
		 *            要代理的对象类型
		 * @param lazyLoader
		 *            懒加载的对象
		 * 
		 * @param configuration
		 *            配置
		 * @param objectFactory
		 *            objectFactory用来初始化对象
		 * @param constructorArgTypes
		 *            构造函数的参数类型
		 * @param constructorArgs
		 *            构造函数的参数值
		 */
		private EnhancedResultObjectProxyImpl(Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration,
				ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
			this.type = type;
			this.lazyLoader = lazyLoader;
			this.aggressive = configuration.isAggressiveLazyLoading();
			this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
			this.objectFactory = objectFactory;
			this.constructorArgTypes = constructorArgTypes;
			this.constructorArgs = constructorArgs;
		}

		/**
		 * 构建target类的代理
		 * 
		 * @param target
		 *            要构建代理的对象
		 * @param lazyLoader
		 *            在DefaultResultSetHandler的getRowValue方法初始化
		 * @param configuration
		 *            配置类
		 * @param objectFactory
		 *            class实例化为object的工场
		 * @param constructorArgTypes
		 *            构造函数的参数类型
		 * @param constructorArgs
		 *            构造函数的参数值
		 * @return
		 */
		public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration,
				ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
			final Class<?> type = target.getClass();
			// 初始化一个EnhancedResultObjectProxyImpl内部类
			EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration,
					objectFactory, constructorArgTypes, constructorArgs);
			// 使用cglib创建代理对象
			Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
			// target是type的初始化对象
			// enhanced可能是type的代理对象
			// 将target对象的值赋值到enhanced，包括父类的字段赋值
			PropertyCopier.copyBeanProperties(type, target, enhanced);
			// 返回代理enhanced
			return enhanced;
		}

		@Override
		public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
			final String methodName = method.getName();
			try {
				synchronized (lazyLoader) {
					if (WRITE_REPLACE_METHOD.equals(methodName)) {
						// 要代理的对象类型对象初始化
						Object original = null;
						// 有参数初始化
						if (constructorArgTypes.isEmpty()) {
							original = objectFactory.create(type);
						} else {
							// 无参数初始化
							original = objectFactory.create(type, constructorArgTypes, constructorArgs);
						}
						// 将sourceBean对象的值赋值到destinationBean，包括父类的字段赋值
						PropertyCopier.copyBeanProperties(type, enhanced, original);
						// lazyLoader.size()保存需要延迟加载属性列表的个数
						if (lazyLoader.size() > 0) {
							return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory,
									constructorArgTypes, constructorArgs);
						} else {
							return original;
						}
					} else {
						// 这里是关键，延迟加载就是调用ResultLoaderMap.loadAll()
						if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
							if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
								// lazyLoader.loadAll
								// 就会触发ResultLoader的loadResult方法完成数据的加载实现。
								lazyLoader.loadAll();
							} else if (PropertyNamer.isProperty(methodName)) {
								// 将get或set方法名获取property字段名
								final String property = PropertyNamer.methodToProperty(methodName);
								if (lazyLoader.hasLoader(property)) {
									lazyLoader.load(property);
								}
							}
						}
					}
				}
				return methodProxy.invoke(enhanced, args);
			} catch (Throwable t) {
				throw ExceptionUtil.unwrapThrowable(t);
			}
		}
	}

	/**
	 * cglib反序列代理实现
	 * 
	 * @author
	 *
	 */
	private static class EnhancedDeserializationProxyImpl extends AbstractEnhancedDeserializationProxy
			implements MethodHandler {
		/**
		 * 构造方法
		 * 
		 * @param type
		 *            原生的对象类型
		 * @param unloadedProperties
		 *            取消的属性集
		 * @param objectFactory
		 *            对象工厂
		 * @param constructorArgTypes
		 *            构造函数的参数类型
		 * @param constructorArgs
		 *            构造函数的参数值
		 */
		private EnhancedDeserializationProxyImpl(Class<?> type,
				Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
				List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
			super(type, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
		}

		/**
		 * 创建代理
		 * 
		 * @param target
		 *            原生的对象类型
		 * @param unloadedProperties
		 *            不加载的属性
		 * @param objectFactory
		 *            对象工厂
		 * @param constructorArgTypes
		 *            构造函数的参数类型
		 * @param constructorArgs
		 *            构造函数的参数值
		 * @return
		 */
		public static Object createProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties,
				ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
			final Class<?> type = target.getClass();
			EnhancedDeserializationProxyImpl callback = new EnhancedDeserializationProxyImpl(type, unloadedProperties,
					objectFactory, constructorArgTypes, constructorArgs);
			Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
			PropertyCopier.copyBeanProperties(type, target, enhanced);
			return enhanced;
		}

		@Override
		public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
			final Object o = super.invoke(enhanced, method, args);
			return (o instanceof AbstractSerialStateHolder) ? o : methodProxy.invoke(o, args);
		}

		@Override
		protected AbstractSerialStateHolder newSerialStateHolder(Object userBean,
				Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory,
				List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
			return new JavassistSerialStateHolder(userBean, unloadedProperties, objectFactory, constructorArgTypes,
					constructorArgs);
		}
	}
}
